package top.rish.utils.common;


import top.rish.utils.excel.anno.ExcelCell;
import top.rish.utils.excel.anno.ExcelRegex;
import top.rish.utils.excel.convert.ExcelDataConvertTo;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * excel list builder
 * rish.top
 */
public class ExcelDateBuilder {
    //日志
    private static final Logger LOGGER = Logger.getLogger();
    // 带@ExcelField注解字段
    private static final Map<Class<?>, Map<String, Field>> BEAN_FIELDS = new ConcurrentHashMap();
    private static final Map<Class<?>, Map<String, ExcelCell>> EXCEL_ANNO = new ConcurrentHashMap<>();

    // 转换器类名
    private static final Map<Class<?>, Map<String, Class<? extends ExcelDataConvertTo>>> BEAN_FIELDS_CONVERT_NAME_CLASS = new ConcurrentHashMap();
    private static final Map<Class<? extends ExcelDataConvertTo>, ExcelDataConvertTo> BEAN_FIELDS_CONVERT = new ConcurrentHashMap();

    // set方法
    private static final Map<Class<?>, Map<String, Method>> BEAN_FIELDS_SET_MOTHOD = new ConcurrentHashMap();

    private static void init(Class<?> clazz) throws Exception {
        if (!BEAN_FIELDS.containsKey(clazz)) {
            Field[] fields = clazz.getDeclaredFields();
            Map<String, Field> fieldMap = new ConcurrentHashMap<>();
            Map<String, Class<? extends ExcelDataConvertTo>> fscs = new ConcurrentHashMap<>();
            Map<String, Method> setMethods = new ConcurrentHashMap<>();
            Map<String, ExcelCell> excelAnno = new ConcurrentHashMap<>();

            for (Field field : fields) {
                field.setAccessible(true);

                ExcelCell excelCell = field.getAnnotation(ExcelCell.class);
                String fieldName = field.getName();
                Class<?> fieldType = field.getType();
                String setMethodName = "set" + StrUtil.upperFirst(fieldName);
                Method method = null;
                try {
                    method = clazz.getDeclaredMethod(setMethodName, fieldType);
                    method.setAccessible(true);
                } catch (Exception e) {
                }

                if ((excelCell != null) && method != null) {
                    String key = null;
                    Class<? extends ExcelDataConvertTo> convert = null;
                    if (excelCell != null) {
                        key = excelCell.row() + "," + excelCell.col();
                        if (excelCell.row() > 0) {
                            String keyTemp = "" + excelCell.col();
                            if (fieldMap.containsKey(key)) {
                                Field field1 = fieldMap.get(key);
                                LOGGER.warn("类:%s 的 %s 与 %s 这两个属性, @ExcelCell注解上row,col  值已重复!都是:%s", clazz.getName(), fieldName, field1.getName(), key);
                            }
                            excelAnno.put(key, excelCell);
                            setMethods.put(key, method);
                            setMethods.put(keyTemp, method);
                            fieldMap.put(key, field);
                            fieldMap.put(keyTemp, field);
                        } else {
                            key = "" + excelCell.col();
                            excelAnno.put(key, excelCell);
                            setMethods.put(key, method);
                            fieldMap.put(key, field);
                        }
                        convert = excelCell.convert();
                    }
                    if (convert != null) {
                        if (excelCell.row() > 0) {
                            fscs.put(key, convert);
                        } else {
                            fscs.put("" + excelCell.col(), convert);
                        }
                        if (!BEAN_FIELDS_CONVERT.containsKey(convert)) {
                            BEAN_FIELDS_CONVERT.put(convert, convert.newInstance());
                        }
                    }


                } else {
                    if (excelCell == null) {
                        LOGGER.warn("类:%s 属性: %s 没有@ExcelCell注解", clazz.getName(), fieldName);
                    }
                    if (method == null) {
                        LOGGER.warn("类:%s缺少%s方法", clazz.getName(), setMethodName);
                    }
                }
            }
            EXCEL_ANNO.put(clazz, excelAnno);
            BEAN_FIELDS.put(clazz, fieldMap);
            BEAN_FIELDS_CONVERT_NAME_CLASS.put(clazz, fscs);
            BEAN_FIELDS_SET_MOTHOD.put(clazz, setMethods);
        }
    }


    private static void buildListOne(List<Object> result,Object t,
                                     Class clazz,
                                     List<String> colIndex,
                                     Map<String,String> data,
                                     String row,int listStartRow) throws Exception {
        Map<String, Field> BEAN_FIELDS_MAP = BEAN_FIELDS.get(clazz);
        Map<String, ExcelCell> annotationMap = EXCEL_ANNO.get(clazz);
        Map<String, Method> SET_METHODS = BEAN_FIELDS_SET_MOTHOD.get(clazz);
        t = clazz.newInstance();
        for (String col : colIndex) {
            String value = data.get(row + "," + col);
            fullValue(Integer.valueOf(row), Integer.valueOf(col), col, listStartRow, annotationMap,
                    value, BEAN_FIELDS_MAP, SET_METHODS, t, clazz);
        }
        for (Map.Entry<String, Field> entry : BEAN_FIELDS_MAP.entrySet()) {
            String key = entry.getKey();
            if (key.contains(",")) {
                String[] split = key.split(",");
                String value = data.get(key);
                fullValue(Integer.valueOf(split[0]), Integer.valueOf(split[1]), key, listStartRow, annotationMap,
                        value, BEAN_FIELDS_MAP, SET_METHODS, t, clazz);
            }
        }
        result.add(t);
    }


    private static Object build(Map<String, String> data, int listStartRow,int listEndRow, Class<?> clazz, boolean isList) throws Exception {
        if (data == null || clazz == null) {
            return null;
        }
        init(clazz);
        Map<String, Field> BEAN_FIELDS_MAP = BEAN_FIELDS.get(clazz);
        if (BEAN_FIELDS == null || BEAN_FIELDS.size() == 0) {
            return null;
        }
        Map<String, ExcelCell> annotationMap = EXCEL_ANNO.get(clazz);
        Map<String, Method> SET_METHODS = BEAN_FIELDS_SET_MOTHOD.get(clazz);

        List<Object> result = new ArrayList<>();
        Object t = clazz.newInstance();
        if (isList) {
            List<String> rowIndex = new ArrayList<>();
            List<String> colIndex = new ArrayList<>();
            for (Map.Entry<String, String> entry : data.entrySet()) {
                String key = entry.getKey();
                String[] split = key.split(",");
                if (!rowIndex.contains(split[0]))
                    rowIndex.add(split[0]);
                if (!colIndex.contains(split[1]))
                    colIndex.add(split[1]);
            }
            Collections.sort(rowIndex, new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return Integer.valueOf(o1) > Integer.valueOf(o2) ? 1 : -1;
                }
            });
            Collections.sort(colIndex, new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return Integer.valueOf(o1) > Integer.valueOf(o2) ? 1 : -1;
                }
            });
            boolean hasStart =   listStartRow > -1  ;
            boolean hasEnd =   listEndRow > -1  ;
            for (String row : rowIndex) {
                if ( hasStart || hasEnd ) {
                    if(hasStart&&hasEnd){
                        if (Integer.valueOf(row) >= listStartRow && Integer.valueOf(row) <= listEndRow) {
                            buildListOne(result,t,clazz,colIndex,data,row,listStartRow);
                        }
                    }else if(hasStart){
                        if (Integer.valueOf(row) >= listStartRow  ) {
                            buildListOne(result,t,clazz,colIndex,data,row,listStartRow);
                        }
                    }else if(hasEnd){
                        if ( Integer.valueOf(row) <= listEndRow) {
                            buildListOne(result,t,clazz,colIndex,data,row,listStartRow);
                        }
                    }
                }
            }
        } else {
            for (Map.Entry<String, Field> entry : BEAN_FIELDS_MAP.entrySet()) {
                String key = entry.getKey();
                if (key.contains(",")) {
                    String[] split = key.split(",");
                    String value = data.get(key);
                    fullValue(Integer.valueOf(split[0]), Integer.valueOf(split[1]), key, 0, annotationMap,
                            value, BEAN_FIELDS_MAP, SET_METHODS, t, clazz);
                }
            }
        }
        return isList ? result : t;
    }


    private static void fullValue(int j, int i, String key, int startRow, Map<String, ExcelCell> annotationMap, String valueObj, Map<String, Field> BEAN_FIELDS_MAP, Map<String, Method> SET_METHODS, Object t,  Class clazz ) throws Exception {
        ExcelCell excelCell = annotationMap.get(key);
        String descript = "";
        boolean required = false;
        ExcelRegex[] excelRegex = null;
        if (excelCell != null) {
            excelRegex = excelCell.regex();
            descript = excelCell.descript();
            required = excelCell.required();
            if (required) {
                if (!StrUtil.isNotBlank(valueObj)) {
                    throw new Exception(String.format("第%s行,第%s列:【%s】不能为空!", j + 1 , i + 1, descript));
                }
            }
            if (excelRegex != null && excelRegex.length > 0) {
                for (ExcelRegex regex : excelRegex) {
                    if (!Pattern.matches(regex.value(), valueObj)) {
                        if (regex.strict()) {
                            throw new Exception(String.format("第%s行,第%s列:【%s】内容:【%s】,不符合正则%s!错误信息:%s", j + 1 , i + 1, descript, valueObj, regex.value(), regex.message()));
                        } else {
                            return;
                        }
                    }
                }
            }
            ExcelDataConvertTo convert = getExcelDataConvert(clazz, key);
            Field field = BEAN_FIELDS_MAP.get(""+i);
            Method setMethod = SET_METHODS.get(key);
            if (field != null && setMethod != null) {
                if (convert != null) {
                    if (convert.required() && !StrUtil.isNotBlank(valueObj)) {
                        throw new Exception(String.format("第%s行,第%s列:【%s】不能为空!", j + 1 , i + 1, descript));
                    }
                    Object convert1 = null;
                    try {
                        convert1 = convert.convert(valueObj);
                    } catch (Exception e) {
                        throw new Exception(String.format("第%s行,第%s列:【%s】内容:【%s】,数据读入出错!错误信息:%s", j + 1 , i + 1, descript, valueObj, e));
                    }
                    setMethod.invoke(t, convert1);
                } else {
                    setMethod.invoke(t, valueObj);
                }
//                LOGGER.info("属性:%s[%s] 已经赋值为:%s", field.getName(), descript, valueObj);
            } else {
                if (field == null) {
                    LOGGER.warn("类:%s 无法装载第%s行第%s列数据", clazz.getName(), j + 1, i+1);
                }
                if (setMethod == null) {
                    LOGGER.warn("类:%s 缺少 %s 方法", clazz.getName(), setMethod);
                }
                return;
            }
        }
        return;
    }


    private static ExcelDataConvertTo getExcelDataConvert(Class<?> clazz, String key) {
        Map<String, Class<? extends ExcelDataConvertTo>> integerClassMap = BEAN_FIELDS_CONVERT_NAME_CLASS.get(clazz);
        if (integerClassMap != null) {
            Class<? extends ExcelDataConvertTo> aClass = integerClassMap.get(key);
            if (aClass != null) {
                return BEAN_FIELDS_CONVERT.get(aClass);
            }
        }
        return null;
    }



    public static <T> List<T> buildList(Map<String, String> read, int listStartRow,int listEndRow, Class<T> clazz) throws Exception {
        return (List<T>) build(read, listStartRow,listEndRow, clazz, true);
    }

    public static <T> T buildBean(Map<String, String> read, int startRow, int endRow, Class<T> clazz) throws Exception {
        return (T) build(read, startRow,endRow, clazz, false);
    }
}
